/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkTypeface.h"
#include "include/private/base/SkTemplates.h"
#include "include/private/base/SkTo.h"
#include "oh/OHDrawingAPI.h"
#include "src/base/SkTLazy.h"
#include "src/base/SkUTF.h"
#include "src/base/SkUtils.h"
#include "src/core/SkDraw.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkMatrixPriv.h"
#include "src/core/SkPaintDefaults.h"
#include "src/core/SkScalerContext.h"
// #include "src/core/SkStrike.h"
// #include "src/core/SkStrikeCache.h"
// #include "src/core/SkStrikeSpec.h"

#include "include/core/SkFontMetrics.h"
#include "include/core/SkPathEffect.h"
using namespace skia_private;

#define kDefault_Size       SkPaintDefaults_TextSize
#define kDefault_Flags      SkFont::kBaselineSnap_PrivFlag
#define kDefault_Edging     SkFont::Edging::kAntiAlias
#define kDefault_Hinting    SkPaintDefaults_Hinting

static inline SkScalar valid_size(SkScalar size) {
    return std::max<SkScalar>(0, size);
}

SkFont::SkFont(sk_sp<SkTypeface> face, SkScalar size, SkScalar scaleX, SkScalar skewX)
    : fTypeface(std::move(face))
    , fSize(valid_size(size))
    , fScaleX(scaleX)
    , fSkewX(skewX)
    , fFlags(kDefault_Flags)
    , fEdging(static_cast<unsigned>(kDefault_Edging))
    , fHinting(static_cast<unsigned>(kDefault_Hinting))
{
    fFontOHOS = OH_Drawing_FontCreate();
    if (fTypeface.get() != nullptr) {
        OH_Drawing_FontSetTypeface(fFontOHOS, *fTypeface);
    }
    OH_Drawing_FontSetTextSize(fFontOHOS, fSize);
    OH_Drawing_FontSetScaleX(fFontOHOS, fScaleX);
    OH_Drawing_FontSetTextSkewX(fFontOHOS, fSkewX);
}

SkFont::SkFont(sk_sp<SkTypeface> face, SkScalar size) : SkFont(std::move(face), size, 1, 0) {}

SkFont::SkFont(sk_sp<SkTypeface> face) : SkFont(std::move(face), kDefault_Size, 1, 0) {}

SkFont::SkFont() : SkFont(nullptr, kDefault_Size) {}

bool SkFont::operator==(const SkFont& b) const {
    return  fTypeface.get() == b.fTypeface.get() &&
            fSize           == b.fSize &&
            fScaleX         == b.fScaleX &&
            fSkewX          == b.fSkewX &&
            fFlags          == b.fFlags &&
            fEdging         == b.fEdging &&
            fHinting        == b.fHinting;
}

SkFont::SkFont(const SkFont& other) {
    fTypeface = other.fTypeface;
    fSize = other.fSize;
    fScaleX = other.fScaleX;
    fSkewX = other.fSkewX;
    fFlags = other.fFlags;
    fEdging = other.fEdging;
    fHinting = other.fHinting;
    fFontOHOS = OH_Drawing_FontCreate();
    if (fTypeface.get() != nullptr) {
        OH_Drawing_FontSetTypeface(fFontOHOS, *fTypeface);
    }
    OH_Drawing_FontSetTextSize(fFontOHOS, fSize);
    OH_Drawing_FontSetScaleX(fFontOHOS, fScaleX);
    OH_Drawing_FontSetTextSkewX(fFontOHOS, fSkewX);
}

SkFont& SkFont::operator=(const SkFont& other) {
    if (this != &other) {
        this->~SkFont();
        new (this) SkFont(other);
    }
    return *this;
}

void SkFont::dump() const {
    SkDebugf("typeface %p\n", fTypeface.get());
    SkDebugf("size %g\n", fSize);
    SkDebugf("skewx %g\n", fSkewX);
    SkDebugf("scalex %g\n", fScaleX);
    SkDebugf("flags 0x%X\n", fFlags);
    SkDebugf("edging %d\n", (unsigned)fEdging);
    SkDebugf("hinting %d\n", (unsigned)fHinting);
}

uint64_t SkFont::hash() const {
    DrawingHash hasher;
    if (fTypeface) {
        hasher.push(fTypeface->hash());
    }
    hasher.push(fSize);
    hasher.push(fScaleX);
    hasher.push(fSkewX);
    hasher.push(fFlags);
    hasher.push(fEdging);
    hasher.push(fHinting);
    return hasher.digest();
}

void SkFont::updateTypefaceWeight() const {
    if (fTypeface == nullptr) {
        return;
    }
    if (!fTypeface->fDefaultStyleUpdated) {
        fTypeface->updateDefaultTypeface();
    }
    if (!(fTypeface->fStyle == fTypeface->fStyleInOHOS) &&
        fTypeface->fontStyle().weight() != SkFontStyle::kNormal_Weight) {
        // 斜体绘制应该通过SetSkewX来控制实现,此处仅配置字重
        auto args = OH_Drawing_FontArgumentsCreate();
        OH_Drawing_FontArgumentsAddVariation(args, "wght", (float)fTypeface->fontStyle().weight());
        auto newT = OH_Drawing_TypefaceCreateFromCurrent(*fTypeface, args);
        if (args) {
            OH_Drawing_FontArgumentsDestroy(args);
        }
        if (newT) {
            fTypeface->setNewTypeface(newT);
        }
    }
    OH_Drawing_FontSetTypeface(fFontOHOS, *fTypeface);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

static inline uint32_t set_clear_mask(uint32_t bits, bool cond, uint32_t mask) {
    return cond ? bits | mask : bits & ~mask;
}

void SkFont::setForceAutoHinting(bool predicate) {
    fFlags = set_clear_mask(fFlags, predicate, kForceAutoHinting_PrivFlag);
    OH_Drawing_FontSetForceAutoHinting(fFontOHOS, predicate);
}
void SkFont::setEmbeddedBitmaps(bool predicate) {
    fFlags = set_clear_mask(fFlags, predicate, kEmbeddedBitmaps_PrivFlag);
    OH_Drawing_FontSetEmbeddedBitmaps(fFontOHOS, predicate);
}
void SkFont::setSubpixel(bool predicate) {
    fFlags = set_clear_mask(fFlags, predicate, kSubpixel_PrivFlag);
    OH_Drawing_FontSetSubpixel(fFontOHOS, predicate);
}
void SkFont::setLinearMetrics(bool predicate) {
    fFlags = set_clear_mask(fFlags, predicate, kLinearMetrics_PrivFlag);
    OH_Drawing_FontSetLinearText(fFontOHOS, predicate);
}
void SkFont::setEmbolden(bool predicate) {
    fFlags = set_clear_mask(fFlags, predicate, kEmbolden_PrivFlag);
    OH_Drawing_FontSetFakeBoldText(fFontOHOS, predicate);
}
void SkFont::setBaselineSnap(bool predicate) {
    fFlags = set_clear_mask(fFlags, predicate, kBaselineSnap_PrivFlag);
    OH_Drawing_FontSetBaselineSnap(fFontOHOS, predicate);
}
void SkFont::setEdging(Edging e) {
    fEdging = SkToU8(e);
    static_assert((int)Edging::kAlias == OH_Drawing_FontEdging::FONT_EDGING_ALIAS, "FontEdging not equal!");
    static_assert((int)Edging::kAntiAlias == OH_Drawing_FontEdging::FONT_EDGING_ANTI_ALIAS, "FontEdging not equal!");
    static_assert((int)Edging::kSubpixelAntiAlias == OH_Drawing_FontEdging::FONT_EDGING_SUBPIXEL_ANTI_ALIAS, "FontEdging not equal!");
    OH_Drawing_FontSetEdging(fFontOHOS, (OH_Drawing_FontEdging)e);
}

void SkFont::setHinting(SkFontHinting h) {
    fHinting = SkToU8(h);
    static_assert((int)SkFontHinting::kNone == OH_Drawing_FontHinting::FONT_HINTING_NONE, "FontEdging not equal!");
    static_assert((int)SkFontHinting::kSlight == OH_Drawing_FontHinting::FONT_HINTING_SLIGHT, "FontEdging not equal!");
    static_assert((int)SkFontHinting::kNormal == OH_Drawing_FontHinting::FONT_HINTING_NORMAL, "FontEdging not equal!");
    static_assert((int)SkFontHinting::kFull == OH_Drawing_FontHinting::FONT_HINTING_FULL, "FontEdging not equal!");
    OH_Drawing_FontSetHinting(fFontOHOS, (OH_Drawing_FontHinting)h);
}

void SkFont::setSize(SkScalar size) {
    fSize = valid_size(size);
    OH_Drawing_FontSetTextSize(fFontOHOS, size);
}
void SkFont::setScaleX(SkScalar scale) {
    fScaleX = scale;
    OH_Drawing_FontSetScaleX(fFontOHOS, scale);
}
void SkFont::setSkewX(SkScalar skew) {
    fSkewX = skew;
    OH_Drawing_FontSetTextSkewX(fFontOHOS, skew);
}

SkFont SkFont::makeWithSize(SkScalar newSize) const {
    SkFont font = *this;
    font.setSize(newSize);
    return font;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

SkScalar SkFont::setupForAsPaths(SkPaint* paint) {
    constexpr uint32_t flagsToIgnore = kEmbeddedBitmaps_PrivFlag |
                                       kForceAutoHinting_PrivFlag;

    fFlags = (fFlags & ~flagsToIgnore) | kSubpixel_PrivFlag;
    this->setHinting(SkFontHinting::kNone);

    if (this->getEdging() == Edging::kSubpixelAntiAlias) {
        this->setEdging(Edging::kAntiAlias);
    }

    if (paint) {
        paint->setStyle(SkPaint::kFill_Style);
        paint->setPathEffect(nullptr);
    }
    SkScalar textSize = fSize;
    this->setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
    return textSize / SkFontPriv::kCanonicalTextSizeForPaths;
}

bool SkFont::hasSomeAntiAliasing() const {
    Edging edging = this->getEdging();
    return edging == SkFont::Edging::kAntiAlias
        || edging == SkFont::Edging::kSubpixelAntiAlias;
}

SkGlyphID SkFont::unicharToGlyph(SkUnichar uni) const {
    SkUnichar utf32Text[1] = { uni };
    SkGlyphID glyphs[1];
    textToGlyphs(utf32Text, sizeof(utf32Text), SkTextEncoding::kUTF32, glyphs, 1);
    return glyphs[0];
//     return this->getTypefaceOrDefault()->unicharToGlyph(uni);
}

void SkFont::unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
    const void* uft32Text = uni;
    size_t byteLength = count * sizeof(SkUnichar);
    textToGlyphs(uft32Text, byteLength, SkTextEncoding::kUTF32, glyphs, count);
//     this->getTypefaceOrDefault()->unicharsToGlyphs(uni, count, glyphs);
}

int SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding,
                         SkGlyphID glyphs[], int maxGlyphCount) const {
//     return this->getTypefaceOrDefault()->textToGlyphs(text, byteLength, encoding,
//                                                       glyphs, maxGlyphCount);
    updateTypefaceWeight();
    return OH_Drawing_FontTextToGlyphs(
            fFontOHOS, text, byteLength, (OH_Drawing_TextEncoding)encoding, glyphs, maxGlyphCount);
}

SkScalar SkFont::measureText(const void *text, size_t length, SkTextEncoding encoding, SkRect *bounds,
                             const SkPaint *paint) const {
    float ret = 0;
    OH_Drawing_ErrorCode errCode;
    SkPaint::Style style = SkPaint::Style::kFill_Style;
    if (paint) {
        paint->updateOHPainter();
        style = paint->getStyle();
    }
    if (!paint || !bounds) {
        errCode = OHDrawingAPI::OH_Drawing_FontMeasureTextWithBrushOrPen(fFontOHOS, text, length,
                                                                         (OH_Drawing_TextEncoding)encoding, nullptr,
                                                                         nullptr, (OH_Drawing_Rect *)bounds, &ret);
    } else {
        *bounds = SkRect::MakeEmpty();
        if (style == SkPaint::Style::kFill_Style || style == SkPaint::Style::kStrokeAndFill_Style) {
            SkRect rect;
            errCode = OHDrawingAPI::OH_Drawing_FontMeasureTextWithBrushOrPen(fFontOHOS, text, length,
                                                                             (OH_Drawing_TextEncoding)encoding, *paint,
                                                                             nullptr, (OH_Drawing_Rect *)&rect, &ret);
            bounds->join(rect);
        }
        if (style == SkPaint::Style::kStroke_Style || style == SkPaint::Style::kStrokeAndFill_Style) {
            SkRect rect;
            errCode = OHDrawingAPI::OH_Drawing_FontMeasureTextWithBrushOrPen(fFontOHOS, text, length,
                                                                             (OH_Drawing_TextEncoding)encoding, nullptr,
                                                                             *paint, (OH_Drawing_Rect *)&rect, &ret);
            bounds->join(rect);
        }
    }

    if (errCode != OH_DRAWING_SUCCESS) {
        SkDebugf("OH_Drawing_FontMeasureText failed, error code: %d", errCode);
    }
    return ret;
//
//     SkAutoToGlyphs atg(*this, text, length, encoding);
//     const int glyphCount = atg.count();
//     if (glyphCount == 0) {
//         if (bounds) {
//             bounds->setEmpty();
//         }
//         return 0;
//     }
//     const SkGlyphID* glyphIDs = atg.glyphs();
//
//     auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this, paint);
//     SkBulkGlyphMetrics metrics{strikeSpec};
//     SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, glyphCount));
//
//     SkScalar width = 0;
//     if (bounds) {
//         *bounds = glyphs[0]->rect();
//         width = glyphs[0]->advanceX();
//         for (int i = 1; i < glyphCount; ++i) {
//             SkRect r = glyphs[i]->rect();
//             r.offset(width, 0);
//             bounds->join(r);
//             width += glyphs[i]->advanceX();
//         }
//     } else {
//         for (auto glyph : glyphs) {
//             width += glyph->advanceX();
//         }
//     }
//
//     if (strikeToSourceScale != 1) {
//         width *= strikeToSourceScale;
//         if (bounds) {
//             bounds->fLeft   *= strikeToSourceScale;
//             bounds->fTop    *= strikeToSourceScale;
//             bounds->fRight  *= strikeToSourceScale;
//             bounds->fBottom *= strikeToSourceScale;
//         }
//     }
//
//     return width;
}

void SkFont::getWidthsBounds(const SkGlyphID glyphIDs[], int count, SkScalar widths[], SkRect bounds[],
                             const SkPaint *paint) const {
    OH_Drawing_Array *rectArray = nullptr;
    if (bounds) {
        rectArray = OHDrawingAPI::OH_Drawing_RectCreateArray(count);
    }

    OH_Drawing_ErrorCode errCode;
    SkPaint::Style style = SkPaint::Style::kFill_Style;
    if (paint) {
        paint->updateOHPainter();
        style = paint->getStyle();
    }
    if (!paint) {
        errCode = OHDrawingAPI::OH_Drawing_FontGetWidthsBounds(fFontOHOS, glyphIDs, count, nullptr, nullptr, widths,
                                                               bounds ? rectArray : nullptr);
        if (bounds && rectArray) {
            for (int i = 0; i < count; i++) {
                OH_Drawing_Rect *rect = nullptr;
                OH_Drawing_ErrorCode err = OHDrawingAPI::OH_Drawing_RectGetArrayElement(rectArray, i, &rect);
                if (err == OH_DRAWING_SUCCESS && rect != nullptr) {
                    bounds[i] = *(SkRect *)rect;
                } else {
                    SkDebugf("OH_Drawing_FontGetWidthsBounds failed, error code: %d rect: %p", errCode, rect);
                }
            }
        }
    } else {
        for (int i = 0; i < count && bounds != nullptr; i++) {
            bounds[i] = SkRect::MakeEmpty();
        }
        if (style == SkPaint::Style::kFill_Style || style == SkPaint::Style::kStrokeAndFill_Style) {
            errCode = OHDrawingAPI::OH_Drawing_FontGetWidthsBounds(fFontOHOS, glyphIDs, count, *paint, nullptr, widths,
                                                                   bounds ? rectArray : nullptr);
            if (bounds && rectArray) {
                OH_Drawing_Rect *rect = nullptr;
                for (int i = 0; i < count; i++) {
                    OHDrawingAPI::OH_Drawing_RectGetArrayElement(rectArray, i, &rect);
                    if (rect == nullptr) {
                        break;
                    }
                    bounds[i].join(*(SkRect *)rect);
                }
            }
        }
        if (style == SkPaint::Style::kStroke_Style || style == SkPaint::Style::kStrokeAndFill_Style) {
            errCode = OHDrawingAPI::OH_Drawing_FontGetWidthsBounds(fFontOHOS, glyphIDs, count, nullptr, *paint, widths,
                                                                   bounds ? rectArray : nullptr);
            if (bounds && rectArray) {
                OH_Drawing_Rect *rect = nullptr;
                for (int i = 0; i < count; i++) {
                    OHDrawingAPI::OH_Drawing_RectGetArrayElement(rectArray, i, &rect);
                    if (rect == nullptr) {
                        break;
                    }
                    bounds[i].join(*(SkRect *)rect);
                }
            }
        }
    }

    if (rectArray) {
        OHDrawingAPI::OH_Drawing_RectDestroyArray(rectArray);
    }
    return;
    
    
//    if (widths != nullptr && bounds == nullptr && paint == nullptr) {
//        OH_Drawing_FontGetWidths(fFontOHOS, glyphIDs, count, widths);
//    } else {
////    OH_Drawing_FontMeasureText(fFontOHOS, const void *text, size_t byteLength, OH_Drawing_TextEncoding
//        //    encoding, OH_Drawing_Rect *bounds, float *textWidth)
//        OH_Drawing_FontMeasureText(fFontOHOS, glyphIDs, count * sizeof(SkGlyphID),
//                                   OH_Drawing_TextEncoding::TEXT_ENCODING_GLYPH_ID, (OH_Drawing_Rect *)bounds,
    //                                   widths);
//    }
//    auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this, paint);
//     SkBulkGlyphMetrics metrics{strikeSpec};
//     SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, count));
//
//     if (bounds) {
//         SkMatrix scaleMat = SkMatrix::Scale(strikeToSourceScale, strikeToSourceScale);
//         SkRect* cursor = bounds;
//         for (auto glyph : glyphs) {
//             scaleMat.mapRectScaleTranslate(cursor++, glyph->rect());
//         }
//     }
//
//     if (widths) {
//         SkScalar* cursor = widths;
//         for (auto glyph : glyphs) {
//             *cursor++ = glyph->advanceX() * strikeToSourceScale;
//         }
//     }
}

void SkFont::getPos(const SkGlyphID glyphIDs[], int count, SkPoint pos[], SkPoint origin) const {
    if (OHDrawingAPI::OH_Drawing_FontGetPos(fFontOHOS, glyphIDs, count, (OH_Drawing_Point *)&origin,
                                            (OH_Drawing_Point2D *)pos) != OH_DRAWING_SUCCESS) {
        for (int i = 0; i < count; i++) {
            pos[i] = {};
        }
    }
//     auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this);
//     SkBulkGlyphMetrics metrics{strikeSpec};
//     SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, count));
//
//     SkPoint sum = origin;
//     for (auto glyph : glyphs) {
//         *pos++ = sum;
//         sum += glyph->advanceVector() * strikeToSourceScale;
//     }
}

void SkFont::getXPos(
        const SkGlyphID glyphIDs[], int count, SkScalar xpos[], SkScalar origin) const {
    SkPoint *pos = new SkPoint[count];
    SkPoint originP = {origin, 0};
    getPos(glyphIDs, count, pos, originP);
    for (int i = 0; i < count; i++) {
        xpos[i] = pos[i].fX;
    }
    delete[] pos;
    return;
    
//     auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this);
//     SkBulkGlyphMetrics metrics{strikeSpec};
//     SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, count));
//
//     SkScalar loc = origin;
//     SkScalar* cursor = xpos;
//     for (auto glyph : glyphs) {
//         *cursor++ = loc;
//         loc += glyph->advanceX() * strikeToSourceScale;
//     }
}

void SkFont::getPaths(const SkGlyphID glyphIDs[], int count, void (*proc)(const SkPath *, const SkMatrix &, void *),
                      void *ctx) const {
    for (int i = 0; i < count; i++) {
        SkPath path;
        getPath(glyphIDs[i], &path);
        proc(&path, SkMatrix::I(), ctx);
    }
//    SkFont font(*this);
//    SkScalar scale = font.setupForAsPaths(nullptr);
//    const SkMatrix mx = SkMatrix::Scale(scale, scale);
//
//    SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font);
//    SkBulkGlyphMetricsAndPaths paths{strikeSpec};
//    SkSpan<const SkGlyph *> glyphs = paths.glyphs(SkSpan(glyphIDs, count));
//
//    for (auto glyph : glyphs) {
//        proc(glyph->path(), mx, ctx);
//    }
}

bool SkFont::getPath(SkGlyphID glyphID, SkPath *path) const {
    OH_Drawing_Path *tmpPath = OH_Drawing_PathCreate();
    auto ret = OHDrawingAPI::OH_Drawing_FontGetPathForGlyph(fFontOHOS, glyphID, tmpPath) == OH_DRAWING_SUCCESS;
    if (ret) {
        path->setOHPath(tmpPath);
    } else {
        OH_Drawing_PathDestroy(tmpPath);
    }
    return ret;
//    struct Pair {
//        SkPath *fPath;
//        bool fWasSet;
//    } pair = {path, false};
//
//    this->getPaths(
//        &glyphID, 1,
//        [](const SkPath *orig, const SkMatrix &mx, void *ctx) {
//            Pair *pair = static_cast<Pair *>(ctx);
//            if (orig) {
//                orig->transform(mx, pair->fPath);
//                pair->fWasSet = true;
//            }
//        },
//        &pair);
//    return pair.fWasSet;
}

std::mutex SkFont::gFontMetricsLock;
SkScalar SkFont::getMetrics(SkFontMetrics* metrics) const {

//     auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this, nullptr);
//
//     SkFontMetrics storage;
//     if (nullptr == metrics) {
//         metrics = &storage;
//     }
//
//     auto cache = strikeSpec.findOrCreateStrike();
//     *metrics = cache->getFontMetrics();
//
//     if (strikeToSourceScale != 1) {
//         SkFontPriv::ScaleFontMetrics(metrics, strikeToSourceScale);
//     }
//     return metrics->fDescent - metrics->fAscent + metrics->fLeading;
    std::lock_guard<std::mutex> lock(gFontMetricsLock);
    return OH_Drawing_FontGetMetrics(fFontOHOS, (OH_Drawing_Font_Metrics*)metrics);
}

SkTypeface* SkFont::getTypefaceOrDefault() const {
    return fTypeface ? fTypeface.get() : SkTypeface::GetDefaultTypeface();
}

sk_sp<SkTypeface> SkFont::refTypefaceOrDefault() const {
    return fTypeface ? fTypeface : SkTypeface::MakeDefault();
}

//////////////////////////////////////////////////////////////////////////////////////////////////

void SkFontPriv::ScaleFontMetrics(SkFontMetrics* metrics, SkScalar scale) {
    metrics->fTop *= scale;
    metrics->fAscent *= scale;
    metrics->fDescent *= scale;
    metrics->fBottom *= scale;
    metrics->fLeading *= scale;
    metrics->fAvgCharWidth *= scale;
    metrics->fMaxCharWidth *= scale;
    metrics->fXMin *= scale;
    metrics->fXMax *= scale;
    metrics->fXHeight *= scale;
    metrics->fCapHeight *= scale;
    metrics->fUnderlineThickness *= scale;
    metrics->fUnderlinePosition *= scale;
    metrics->fStrikeoutThickness *= scale;
    metrics->fStrikeoutPosition *= scale;
}

 SkRect SkFontPriv::GetFontBounds(const SkFont& font) {
//     SkMatrix m;
//     m.setScale(font.getSize() * font.getScaleX(), font.getSize());
//     m.postSkew(font.getSkewX(), 0);
//
//     SkTypeface* typeface = font.getTypefaceOrDefault();
//
//     SkRect bounds;
//     m.mapRect(&bounds, typeface->getBounds());
//     return bounds;
    return {};
 }

SkScalar SkFontPriv::ApproximateTransformedTextSize(const SkFont& font, const SkMatrix& matrix,
                                                    const SkPoint& textLocation) {
    if (!matrix.hasPerspective()) {
        return font.getSize() * matrix.getMaxScale();
    } else {
        // approximate the scale since we can't get it directly from the matrix
        SkScalar maxScaleSq = SkMatrixPriv::DifferentialAreaScale(matrix, textLocation);
        if (SkScalarIsFinite(maxScaleSq) && !SkScalarNearlyZero(maxScaleSq)) {
            return font.getSize() * SkScalarSqrt(maxScaleSq);
        } else {
            return -font.getSize();
        }
    }
}

int SkFontPriv::CountTextElements(const void* text, size_t byteLength, SkTextEncoding encoding) {
    switch (encoding) {
        case SkTextEncoding::kUTF8:
            return SkUTF::CountUTF8(reinterpret_cast<const char*>(text), byteLength);
        case SkTextEncoding::kUTF16:
            return SkUTF::CountUTF16(reinterpret_cast<const uint16_t*>(text), byteLength);
        case SkTextEncoding::kUTF32:
            return byteLength >> 2;
        case SkTextEncoding::kGlyphID:
            return byteLength >> 1;
    }
    SkASSERT(false);
    return 0;
}

// void SkFontPriv::GlyphsToUnichars(const SkFont& font, const SkGlyphID glyphs[], int count,
//                                   SkUnichar text[]) {
//     if (count <= 0) {
//         return;
//     }
//
//     auto typeface = font.getTypefaceOrDefault();
//     const unsigned numGlyphsInTypeface = typeface->countGlyphs();
//     AutoTArray<SkUnichar> unichars(numGlyphsInTypeface);
//     typeface->getGlyphToUnicodeMap(unichars.get());
//
//     for (int i = 0; i < count; ++i) {
//         unsigned id = glyphs[i];
//         text[i] = (id < numGlyphsInTypeface) ? unichars[id] : 0xFFFD;
//     }
// }
